﻿#include <regex>
#include <filesystem>
#include "image_io.h"

namespace fs = std::filesystem;

LiveLoader* LiveLoader::Build(int cam_id) {
	auto loader = new LiveLoader;
#if defined(WIN32)
	loader->cap_.open(cam_id, cv::CAP_DSHOW); //open camera faster with DXSHOW on windows
#else
	loader->cap_.open(cam_id);
#endif
	if (loader->cap_.isOpened()) {
		loader->width_ = (int)loader->cap_.get(cv::CAP_PROP_FRAME_WIDTH);
		loader->height_ = (int)loader->cap_.get(cv::CAP_PROP_FRAME_HEIGHT);
		return loader;
	}
	else {
		delete loader;
		return nullptr;
	}
}

bool LiveLoader::FetchImage() {
	bool ret = false;
	ret = cap_.read(image_);
	frame_index_++;
	return ret;
}

VideoLoader::VideoLoader(const std::string& frames) {
	if (cap_.open(frames)) {
		width_ = (int)cap_.get(cv::CAP_PROP_FRAME_WIDTH);
		height_ = (int)cap_.get(cv::CAP_PROP_FRAME_HEIGHT);
	}
}

bool VideoLoader::FetchImage() {
	bool ret = false;
	ret = cap_.read(image_);
	frame_index_++;
	return ret;
}

void ListFrame(const std::string& dir_path, const std::regex& fileSuffix, std::vector<std::string>& file_list) {
	//regex fileSuffix("(.*).(.jpg)");
	//regex fileSuffix(".*z.*\\.(jpg|png)");
	file_list = std::vector<std::string>();
	for (auto& DirectoryIter : fs::directory_iterator(dir_path)) {
		auto filepath = DirectoryIter.path();
		auto filename = filepath.filename();

		if (std::regex_match(filename.string(), fileSuffix)) {
			file_list.push_back(filepath.string());
		}
	}
	std::sort(file_list.begin(), file_list.end());
}

FolderImageLoader* FolderImageLoader::Build(
	const std::filesystem::path& image_dir,
	const std::string& ext)
{
	std::string reg_str = "(.*)(." + ext + ")";
	std::regex file_suffix(reg_str);
	std::vector<std::string> files;
	ListFrame(image_dir.string(), file_suffix, files);

	if (0 == files.size()) {
		std::cerr << "[ERROR] FolderImageLoader::Build | no images in folder : "
			<< image_dir << std::endl;
		return nullptr;
	}

	cv::Mat frame = cv::imread(files[0]);
	if (frame.empty()) {
		std::cerr << "[ERROR] FolderImageLoader::Build | fail to load #0 image"
			<< image_dir << std::endl;
		return nullptr;
	}

	FolderImageLoader* loader = new FolderImageLoader;
	loader->files_ = files;
	loader->frame_count_ = files.size();
	loader->width_ = frame.cols;
	loader->height_ = frame.rows;
	return loader;
}

bool FolderImageLoader::FetchImage() {
	if (frame_index_ == frame_count_)
		return false;

	image_ = cv::imread(files_[frame_index_]);
	if (image_.empty())
		return false;

	frame_index_++;
	return true;
}

VideoImageWriter* VideoImageWriter::Build(
	const std::filesystem::path& video_path, 
	int width, int height) 
{
	auto writer = new VideoImageWriter;
	writer->writer_.open(
		video_path.string(),
		cv::VideoWriter::fourcc('m', 'p', '4', 'v'), 30,
		cv::Size(width, height));
	if (writer->writer_.isOpened()) {
		writer->width_ = width;
		writer->height_ = height;
		return writer;
	} 
	else {
		std::cerr << "[ERROR] VideoWriter fail to open video: "
			<< video_path << std::endl;
		delete writer;
		return nullptr;
	}
	
}

bool VideoImageWriter::WriteImage(const cv::Mat& image) {
	if (image.empty()) {
		std::cerr << "[ERROR] VideoWriter::WriteImage cannot write empty image"
			<< std::endl;
		return false;
	}
	writer_ << image;
	return true;
}